local Msger = require "core.Msger"
local WzqConst = require "modules.wzq.WzqConst"
local WzqProto = require "modules.wzq.WzqProto"

local M = {}

---@type Room
local room
---@type RoomData
local roomData
local gameStatus = WzqConst.GAME_STATUS.READYING

-- 游戏需要的玩家人数
local GAME_PLAYER_COUNT = 2

local players = {}
local playersById = {}
-- 当前操作的玩家座位
local curOpSeat

local readyPlayers = {}
local readyCount = 0

local winner

local map = {}
local MAX_X = 10
local MAX_Y = 10

local LINES = {{{0, 1}, {0, -1}}, -- 上下
{{-1, 0}, {1, 0}}, -- 左右
{{-1, 1}, {1, -1}}, -- 左上，右下
{{-1, -1}, {1, 1}} -- 左下，右上
}

local function resetMap()
    map = {}
    for x = 1, MAX_X do
        map[x] = {}
    end
end

local function resetGame()
    resetMap()
    readyPlayers = {}
    readyCount = 0
    gameStatus = WzqConst.GAME_STATUS.READYING
end

function M.CS_WZQ_QUERY(playerId)
    local result = {
        roomId = roomData.id,
        owner = roomData.owner,
        players = players,
        gameStatus = gameStatus,
        seat = playersById[playerId].roomPlayer.seat
    }

    if gameStatus == WzqConst.GAME_STATUS.READYING then
        result.readySeats = table.keys(readyPlayers)
    elseif gameStatus == WzqConst.GAME_STATUS.GAMING then
        result.curOpSeat = curOpSeat
        result.map = map
    else
        result.winner = winner
    end

    return result
end

local function seatById(playerId)
    local player = playersById[playerId]
    if not player then
        return
    end

    return player.roomPlayer.seat
end

function M.CS_WZQ_READY(playerId)
    local seat = seatById(playerId)
    if not seat then
        return
    end

    if readyPlayers[seat] then
        return WzqConst.READY_RESULT_CODE.ALREADY_READY
    end

    if gameStatus ~= WzqConst.GAME_STATUS.READYING then
        return WzqConst.READY_RESULT_CODE.GAMING
    end

    readyPlayers[seat] = true
    readyCount = readyCount + 1
    if readyCount == GAME_PLAYER_COUNT then
        M.startGame()
    else
        Msger.broadRoom(WzqProto.WZQ_READY, {
            seat = seat
        })
    end
end

function M.CS_WZQ_UNREADY(playerId)
    local seat = seatById(playerId)
    if not seat then
        return
    end

    if gameStatus ~= WzqConst.GAME_STATUS.READYING then
        return WzqConst.UNREADY_RESULT_CODE.GAMING
    end

    if not readyPlayers[seat] then
        return WzqConst.UNREADY_RESULT_CODE.NOT_READY
    end

    readyPlayers[seat] = nil
    readyCount = readyCount - 1
    Msger.broadRoom(WzqProto.WZQ_UNREADY, {
        seat = seat
    })
end

function M.canPlayerEnter(seat, playerId)
    return gameStatus == WzqConst.GAME_STATUS.READYING
end

-- 玩家进入游戏
function M.onPlayerEnter(seat, playerId)
    local player = {
        roomPlayer = roomData.players[seat]
    }

    players[seat] = player
    playersById[playerId] = player
    Msger.broadWithoutId(playerId, WzqProto.WZQ_PLAYER_ENTER, {
        seat = seat,
        player = players[seat]
    })
end

function M.canPlayerExit(seat)
    if gameStatus ~= WzqConst.GAME_STATUS.GAMING then
        return false
    end

    return true
end

function M.playerExit(seat)
    playersById[players[seat].roomPlayer.playerInfo.id] = nil
    players[seat] = nil
    readyPlayers[seat] = nil
    --- 通知其他玩家有人退出
    Msger.broadWithoutId(players[seat].id, WzqProto.WZQ_PLAYER_EXIT, {
        seat = seat
    })
end

local function isGameOver(seat, pieceX, pieceY)
    for _, line in ipairs(LINES) do
        local cnt = 1
        for _, direct in ipairs(line) do
            local offsetX, offsetY = direct[1], direct[2]
            local x, y = pieceX + offsetX, pieceY + offsetY
            while Counter.between(x, 1, MAX_X) and Counter.between(y, 1, MAX_Y) do
                if map[x][y] ~= seat then
                    break
                end

                cnt = cnt + 1
                if cnt >= 5 then
                    return true
                end

                x, y = x + offsetX, y + offsetY
            end
        end
    end

    return false
end

-- 游戏开始
function M.startGame()
    gameStatus = WzqConst.GAME_STATUS.GAMING
    curOpSeat = 1
    Msger.broadRoom(WzqProto.WZQ_GAME_START, {
        curOpSeat = curOpSeat
    })
end

function M.CS_WZQ_DOWN_PIECE(playerId, protoData)
    if curOpSeat ~= seatById(playerId) then
        return WzqConst.DOWN_PIECE_RESULT_CODE.NOT_YOUR_TURN
    end

    local x = protoData.x
    local y = protoData.y
    if x > MAX_X or x < 1 or y > MAX_Y or y < 1 then
        return {
            code = WzqConst.DOWN_PIECE_RESULT_CODE.OUT_OF_BOUND
        }
    end

    if map[x][y] then
        return {
            code = WzqConst.DOWN_PIECE_RESULT_CODE.NOT_EMPTY
        }
    end

    map[x][y] = curOpSeat
    local downPieceRet = {
        piece = {
            x = x,
            y = y
        }
    }

    if not isGameOver(curOpSeat, x, y) then
        curOpSeat = curOpSeat + 1
        if curOpSeat > GAME_PLAYER_COUNT then
            curOpSeat = 1
        end

        downPieceRet.nextOpSeat = curOpSeat
        Msger.broadRoom(WzqProto.WZQ_DOWN_PIECE, downPieceRet)
        return
    end

    Msger.broadRoom(WzqProto.WZQ_DOWN_PIECE, downPieceRet)
    gameStatus = WzqConst.GAME_STATUS.GAME_SETTLE
    winner = curOpSeat
    Msger.broadRoom(WzqProto.WZQ_GAME_SETTLE, {
        winner = curOpSeat
    })

    resetGame()
end

function M.gameSettle()

end

function M.init(roomObj)
    room = roomObj
    roomData = room.room
    for k, v in pairs(roomData.players) do
        local player = {
            roomPlayer = v
        }

        players[v.seat] = player
        playersById[v.playerInfo.id] = player
    end

    resetGame()
end

return M
